﻿using Microsoft.AspNetCore.Http;
using SBlogCore.Common.App.Options;
using SBlogCore.Common.FriendlyException;
using SBlogCore.Common.Snowflake;

namespace SBlogCore.Common.FileUploadExtend
{
    public abstract class FileUploadStrategyAbstract
    {
        /// <summary>
        /// 保存文件的文件夹
        /// </summary>
        private string? _updateFilePath;
        private FileUploadContext? _context;
        /// <summary>
        /// 文件大小Bytes
        /// </summary>
        public long FileSize => _context?.FileLength??0;
        /// <summary>
        /// 上传文件限制大小/M
        /// </summary>
        public long FileSizeMax { get; protected set; } = 5;

        /// <summary>
        /// 最大大小
        /// </summary>
        public long SizeMax => FileSizeMax * 1024 * 1024;

        /// <summary>
        /// 文件类型
        /// </summary>
        private string? _fileType;

        /// <summary>
        /// 加载配置选项
        /// </summary>
        /// <param name="option"></param>
        /// <param name="updateFilePath"></param>
        public void LoadOption(FileOption option,string updateFilePath)
        {
            _updateFilePath = updateFilePath;
            _fileType = option.FileType;
            if (string.IsNullOrEmpty(_fileType))
                throw SBlogCoreException.Say("请选择文件类型");
            FileSizeMax = option.SizeMax??5;
        }

        


        /// <summary>
        /// 保存文件
        /// </summary>
        /// <param name="file"></param>
        /// <exception cref="Exception"></exception>
        /// <returns></returns>
        public async Task SaveAsync(IFormFile file)
        {
            try
            {
                FileUploadContext context = new FileUploadContext(file);
                _context = context;
                //文件检测
                FileRules(context);
                //IFormFile file = context.FormFile;
                var fileBuffers = await ReadFileToStreamAsync(context);
                //创建文件路径
                var uploadPath = CreateFileUploadPath();
                //为文件命名
                CreateFileNewName(context);
                //拼接最终要保存的路径
                var path = Path.Combine( uploadPath, Name);
                //写入文件
                await WriteStreamToFileAsync(Path.Combine(Directory.GetCurrentDirectory(),path), fileBuffers);
                FilePath = path;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }

        /// <summary>
        /// 文件名称
        /// </summary>
        public string Name { get;private set; } = string.Empty;

        /// <summary>
        /// 文件检测
        /// </summary>
        /// <param name="context"></param>
        private void FileRules(FileUploadContext context)
        {
            if (context.FileLength == 0)
                throw SBlogCoreException.Say("文件数据不能为空");
            if (context.FileLength > SizeMax)
                throw SBlogCoreException.Say($"文件不能大于{FileSizeMax}M");
            UploadFileBefore(context);
        }
        /// <summary>
        /// 验证完之后的上传前
        /// </summary>
        /// <param name="context"></param>
        protected virtual void UploadFileBefore(FileUploadContext context)
        {

        }

        /// <summary>
        /// 为新文件命名
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private void CreateFileNewName(FileUploadContext context)
        {
            var ext = Path.GetExtension(context.GetFileName).ToLower();
            string newName = $"{IDGenerator.NextId()}{ext}";
            newName = RewriteFileName?.Invoke(newName) ?? newName;
            Name = newName;
        }

        /// <summary>
        /// 创建文件保存路径
        /// </summary>
        /// <returns></returns>
        private string CreateFileUploadPath()
        {
            if (string.IsNullOrWhiteSpace(_updateFilePath))
            {
                _updateFilePath = "wwwroot";
            }
            var folder = DateTime.Now.ToString("yyyyMMdd");
            var uploadPath = Path.Combine(_updateFilePath, folder);
            uploadPath = RewriteUploadPath?.Invoke(uploadPath) ?? uploadPath;
            CreateDirectory(uploadPath);
            return uploadPath;
        }

        /// <summary>
        /// 读取为文件流
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        protected virtual Task<byte[]> ReadFileToStreamAsync(FileUploadContext context)
        {
            return context.ReadFileToStream();
        }

        /// <summary>
        /// 重写上传路径
        /// </summary>
        public Func<string, string>? RewriteUploadPath;
        /// <summary>
        /// 重写文件名
        /// </summary>
        public Func<string, string>? RewriteFileName;

        /// <summary>
        /// 写流为文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="result"></param>
        protected virtual async Task WriteStreamToFileAsync(string path, byte[] result)
        {
            //using var fs = new FileStream(path, FileMode.Create);
            //fs.Write(fileBuffers, 0, fileBuffers.Length);
            //fs.Close();
            await using var sourceStream = File.Open(path, FileMode.OpenOrCreate);
            sourceStream.Seek(0, SeekOrigin.End);
            await sourceStream.WriteAsync(result, 0, result.Length);
        }

        /// <summary>
        ///文件路径
        /// </summary>
        private string? _filePath;
        public string? FilePath
        {
            get
            {
                var filePath = _filePath;
                if (!string.IsNullOrWhiteSpace(_filePath) && _filePath.StartsWith("wwwroot\\"))
                {
                    filePath= _filePath.Replace("wwwroot\\", "");
                }
                return $"\\{filePath}";
            }
            private set => _filePath = value;
        }

        /// <summary>
        ///Web路径
        /// </summary>
        public string WebPath => !string.IsNullOrWhiteSpace(FilePath) ? $"{FilePath.Replace('\\', '/')}" : string.Empty;

        /// <summary>
        ///文件夹不存在则创建文件夹
        /// </summary>
        /// <param name="uploadPath"></param>
        private static void CreateDirectory(string uploadPath)
        {
            if (!Directory.Exists(uploadPath))
            {
                Directory.CreateDirectory(uploadPath);
            }
        }
    }
}
